import time
import os
from selenium.common.exceptions import NoSuchElementException, NoSuchFrameException, NoSuchAttributeException
from frame_work.logger import Logger
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select
from frame_work.setting import screenshot_Path

logger = Logger(__name__).get_logger()


class BasePage(object):
    """
    定义一个页面基类，让所有页面都继承这个类，封装一些常用的页面操作方法到这个类
    """

    def __init__(self, driver):
        self.driver = driver
        # self.logger = Logger(logger="BasePage").get_logger()

    # quit browser and end testing
    def quit_browser(self):
        self.driver.quit()

    # 浏览器前进操作
    def forward(self):
        self.driver.forward()
        logger.debug("在当前页面点击浏览器前进操作")

    # 浏览器后退操作
    def back(self):
        self.driver.back()
        logger.debug("在当前页面点击浏览器后退操作")

    # 隐式等待
    def wait(self, seconds):
        self.driver.implicitly_wait(seconds)
        logger.debug("隐式等待 %d 秒" % seconds)

    # 关闭当前浏览器窗口
    def close(self):
        self.driver.close()
        logger.debug("关闭当前浏览器标签页")

    # 关闭浏览器
    def quit(self):
        self.driver.quit()
        logger.debug("关闭浏览器")

    # html页面源码
    def page_source(self):
        logger.debug("获取当前HTML页面源码")
        return self.driver.page_source

    # 保存图片
    def get_windows_img(self, case_name=None):
        rq = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
        screen_name = case_name + '_' + rq + '.png'
        screen_path = os.path.join(
            screenshot_Path, 'screenshot_' + screen_name)
        try:
            self.driver.get_screenshot_as_file(screen_path)
            logger.debug("截屏成功且已将图片 %s 保存到/screenshots目录下" % screen_name)
        except BaseException as e:
            logger.error("截屏失败 reason %s" % e)

    # 定位一组元素方法
    def find_element(self, selector):
        """
         这个地方为什么是根据=>来切割字符串，请看页面里定位元素的方法
         submit_btn = "id=>su"
         login_lnk = "xpath => //*[@id='u1']/a[7]"  # 百度首页登录链接定位
         如果采用等号，结果很多xpath表达式中包含一个=，这样会造成切割不准确，影响元素定位
        :param selector:
        :return: element
        """
        element = ''
        if '=>' not in selector:
            return self.driver.find_element_by_id(selector)
        selector_By = selector.split('=>')[0]
        selector_Value = selector.split('=>')[1]
        try:
            if selector_By == "i" or selector_By == 'id':
                element = self.driver.find_element_by_id(selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位到..." %
                    (selector_By, selector_Value))
            elif selector_By == "n" or selector_By == 'name':
                element = self.driver.find_element_by_name(selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位到..." %
                    (selector_By, selector_Value))
            elif selector_By == "c" or selector_By == 'class_name':
                element = self.driver.find_element_by_class_name(
                    selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位到..." %
                    (selector_By, selector_Value))
            elif selector_By == "l" or selector_By == 'link_text':
                element = self.driver.find_element_by_link_text(selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位到..." %
                    (selector_By, selector_Value))
            elif selector_By == "p" or selector_By == 'partial_link_text':
                element = self.driver.find_element_by_partial_link_text(
                    selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位到..." %
                    (selector_By, selector_Value))
            elif selector_By == "t" or selector_By == 'tag_name':
                element = self.driver.find_element_by_tag_name(selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位到..." %
                    (selector_By, selector_Value))
            elif selector_By == "x" or selector_By == 'xpath':
                element = self.driver.find_element_by_xpath(selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位到..." %
                    (selector_By, selector_Value))
            elif selector_By == "s" or selector_By == 'selector_selector':
                element = self.driver.find_element_by_css_selector(
                    selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位到..." %
                    (selector_By, selector_Value))
        except NoSuchElementException as e:
            logger.debug("NoSuchElementException: %s" % e)
            return None
        return element

    # elements 返回的是多组webelement
    def find_multi_elements(self, selector):
        """
        定位多个元素
        :param selector:
        :return:
        """
        elements = ''
        if '=>' not in selector:
            return self.driver.find_elements_by_id(selector)
        selector_By = selector.split('=>')[0]
        selector_Value = selector.split('=>')[1]
        try:
            if selector_By == "i" or selector_By == 'id':
                elements = self.driver.find_elements_by_id(selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位多组webelement..." %
                    (selector_By, selector_Value))
            elif selector_By == "n" or selector_By == 'name':
                elements = self.driver.find_elements_by_name(selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位多组webelement..." %
                    (selector_By, selector_Value))
            elif selector_By == "c" or selector_By == 'class_name':
                elements = self.driver.find_elements_by_class_name(
                    selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位多组webelement..." %
                    (selector_By, selector_Value))
            elif selector_By == "l" or selector_By == 'link_text':
                elements = self.driver.find_elements_by_link_text(
                    selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位多组webelement..." %
                    (selector_By, selector_Value))
            elif selector_By == "p" or selector_By == 'partial_link_text':
                elements = self.driver.find_elements_by_partial_link_text(
                    selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位多组webelement..." %
                    (selector_By, selector_Value))
            elif selector_By == "t" or selector_By == 'tag_name':
                elements = self.driver.find_elements_by_tag_name(
                    selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位多组webelement..." %
                    (selector_By, selector_Value))
            elif selector_By == "x" or selector_By == 'xpath':
                elements = self.driver.find_elements_by_xpath(selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位多组webelement..." %
                    (selector_By, selector_Value))
            elif selector_By == "s" or selector_By == 'selector_selector':
                elements = self.driver.find_elements_by_css_selector(
                    selector_Value)
                logger.debug(
                    "已通过%s以及值%s的方式定位多组webelement..." %
                    (selector_By, selector_Value))
        except NoSuchElementException as e:
            logger.error("NoSuchElementException: %s" % e)
            return None
        return elements

    # 向输入框中输入文本
    def type(self, selector, text):
        el = self.find_element(selector)
        # el.clear()
        try:
            el.send_keys(text)
            logger.debug("在输入框中输入 \' %s \'" % text)
        except BaseException as e:
            logger.debug("输入失败，reason：%s" % e)

    # 清除文本框
    def clear(self, selector):
        el = self.find_element(selector)
        try:
            el.clear()
            logger.debug("在输入框输入数据之前执行清除内容操作")
        except BaseException as e:
            logger.error("清除内容失败 reason： %s" % e)

    # 特例，专门针对口令下单的方法
    def type_command_sendkeys(self, selector, text):
        selector_By = selector.split('=>')[0]
        selector_Value = selector.split('=>')[1]
        if selector_By == "id":
            self.driver.find_element_by_id(selector_Value).send_keys(text)
            logger.debug(
                "口令下单通过 %s 及值 %s 的方式定位" %
                (selector_By, selector_Value))
        elif selector_By == "name":
            self.driver.find_element_by_name(selector_Value).send_keys(text)
            logger.debug(
                "口令下单通过 %s 及值 %s 的方式定位" %
                (selector_By, selector_Value))
        else:
            logger.error("Fail")

    # 定位某个元素
    def find(self, selector):
        el = self.find_element(selector)
        logger.debug("已找到该元素 %s " % selector)

    # 点击元素
    def click(self, selector):
        el = self.find_element(selector)
        try:
            el.click()
            logger.debug("点击元素 %s" % selector)
        except BaseException as e:
            logger.debug("点击元素 %s 失败 reason:%s" % (selector, e))

    # 针对发布活动来源不好定位，采用elements，然后切割选择自己想要的
    def source_Click(self, selector, index):
        els = self.find_multi_elements(selector)[index]
        try:
            els.click()
            logger.debug("点击元素 %s" % selector)
        except BaseException as e:
            logger.error("点击元素 %s 失败 reason:%s" % (selector, e))

    # iframe切换
    def switch_frame(self, text):
        try:
            if text == 'default':
                self.driver.switch_to.parent_frame()
            else:
                self.driver.switch_to.frame(text)
            logger.debug("iframe切换")
        except NoSuchFrameException as e:
            logger.error("iframe切换失败,reason: %s" % e)

    # 操作Windows命令，打开某一文件
    def os_system(self, text, image_path=None):
        try:
            os.system(text % image_path)
            logger.debug("打开windows窗口")
        except BaseException as e:
            logger.error("打开失败,reason: %s" % e)

    # 页面title
    def get_page_title(self):
        logger.debug("获取当前页面title: %s" % self.driver.title)
        return self.driver.title

    # 页面URL
    def get_page_url(self):
        logger.debug("获取当前页面url: %s" % self.driver.current_url)
        return self.driver.current_url

    # 元素文本文字
    def text(self, selector):
        try:
            el = self.find_element(selector).text
            logger.debug("获取元素值\' %s \'." % el)
            return el
        except BaseException as e:
            logger.debug("获取元素值失败")
            return False

    # 通过某一属性来定位get_attribute()
    def get_attribute(self, selector, type):
        try:
            el = self.find_element(selector).get_attribute(type)
            logger.debug("获取元素属性\' %s \'." % el)
            return el
        except NoSuchAttributeException as e:
            logger.error("获取元素属性失败,reason : %s" % e)
            return False

    # 执行js脚本，可传参数
    def execute_script(self, script, *args):
        try:
            logger.debug("执行js脚本")
            self.driver.execute_script(script)
        except BaseException as e:
            logger.debug("js执行失败！原因：%s" % e)
            return False

    # 模拟鼠标行为_鼠标移动到指定位置
    def action_chains_mte(self, selector):
        ele = self.find_element(selector)
        try:
            ActionChains(self.driver).move_to_element(
                ele).perform()  # perform前可以有click操作
            logger.debug("鼠标移动到指定位置")
        except BaseException as e:
            logger.debug("模拟鼠标操作异常！原因：%s" % e)
            return False

    # 模拟鼠标行为_拖拽
    def action_Chains_DAD(self, source_selector, target_selector):
        source_ele = self.find_element(source_selector)
        target_ele = self.find_element(target_selector)
        try:
            ActionChains(
                self.driver).drag_and_drop(
                source_ele,
                target_ele).perform()
            logger.debug("将元素拖拽到指定位置上")
        except BaseException as e:
            logger.debug("模拟鼠标操作异常！原因：%s" % e)
            return False

    # 模拟键盘行为
    def action_Keys(self, selector, behavior, letter=None):
        ele = self.find_element(selector)
        try:
            if behavior == 'SPACE':
                ele.send_keys(Keys.SPACE)  # 空格键
                logger.debug("按空格键")
            elif behavior == 'ENTER':
                ele.send_keys(Keys.ENTER)  # 回车键
                logger.debug("按回车键")
            elif behavior == 'BACK_SPACE':
                ele.send_keys(Keys.BACK_SPACE)  # 回退键
                logger.debug("按回退键")
            elif behavior == 'TAB':
                ele.send_keys(Keys.TAB)  # 制表键
                logger.debug("按制表键")
            elif behavior == 'ALT':
                ele.send_keys(Keys.ALT)  # ALT键
                logger.debug("按ALT键")
            elif behavior == 'ESCAPE':
                ele.send_keys(Keys.ESCAPE)  # 返回键
                logger.debug("按返回键")
            elif behavior == 'PAGE_UP':
                ele.send_keys(Keys.PAGE_UP)  # 翻页键上
                logger.debug("按翻页键上")
            elif behavior == 'PAGE_DOWN':
                ele.send_keys(Keys.PAGE_DOWN)  # 翻页键下
                logger.debug("按翻页键下")
            elif behavior == 'END':
                ele.send_keys(Keys.END)  # 行尾键
                logger.debug("按行尾键")
            elif behavior == 'HOME':
                ele.send_keys(Keys.HOME)  # 行首键
                logger.debug("按行首键")
            elif behavior == 'LEFT':
                ele.send_keys(Keys.LEFT)  # 方向键左
                logger.debug("按方向键左")
            elif behavior == 'UP':
                ele.send_keys(Keys.UP)  # 方向键上
                logger.debug("按方向键上")
            elif behavior == 'RIGHT':
                ele.send_keys(Keys.RIGHT)  # 方向键右
                logger.debug("按方向键右")
            elif behavior == 'DOWN':
                ele.send_keys(Keys.DOWN)  # 方向键下
                logger.debug("按方向键下")
            elif behavior == 'INSERT':
                ele.send_keys(Keys.INSERT)  # 插入键
                logger.debug("按插入键")
            elif behavior == 'DELETE':
                ele.send_keys(Keys.DELETE)  # 删除键
                logger.debug("按删除键")
            elif behavior == 'CONTROL' and letter == 'a':
                ele.send_keys(Keys.CONTROL, 'a')  # 全选
                logger.debug("按全选")
            elif behavior == 'CONTROL' and letter == 'c':
                ele.send_keys(Keys.CONTROL, 'c')  # 复制
                logger.debug("执行复制操作")
            elif behavior == 'CONTROL' and letter == 'v':
                ele.send_keys(Keys.CONTROL, 'v')  # 粘贴
                logger.debug("执行粘贴操作")
            elif behavior == 'CONTROL' and letter == 'x':
                ele.send_keys(Keys.CONTROL, 'x')  # 剪切
                logger.debug("执行剪切操作")
            elif behavior == 'CONTROL' and letter == 'z':
                ele.send_keys(Keys.CONTROL, 'a')  # 撤销
                logger.debug("执行撤销操作")
        except BaseException as e:
            logger.debug("键盘操作异常！原因：%s" % e)
            return False

    # 下拉框select方法
    def select_choice(self, selector, type, value=None):
        ele = self.find_element(selector)
        try:
            if type == 'index':
                Select(ele).select_by_index(value)
                logger.debug("下拉框已经通过 %s 元素成功选择元素" % type)
            elif type == 'value':
                Select(ele).select_by_value(value)
                logger.debug("下拉框已经通过 %s 元素成功选择元素" % type)
            elif type == 'text':
                Select(ele).select_by_visible_text(value)
                logger.debug("下拉框已经通过 %s 元素成功选择元素" % type)
            elif type == 'option':
                first_options = Select(ele).first_selected_option.text
                logger.debug("输出下拉框中第一个被选中的option: %s" % first_options)
                return first_options
            elif type == 'options':
                s = Select(ele)
                all_selected_op = [
                    select.text for select in s.all_selected_options]
                logger.debug("输出下拉框中所有被选中的options: %s" % all_selected_op)
                return all_selected_op
            elif type == 'all':
                s = Select(ele)
                options = [select.text for select in s.options]
                logger.debug("输出下拉框中所有options: %s" % options)
                return options
        except BaseException as e:
            logger.error("下拉框选择元素失败,reason : %s" % e)
            return False

    # 下拉框select取消方法
    def deselect_choice(self, selector, type, value=None):
        ele = self.find_element(selector)
        try:
            if type == 'index':
                Select(ele).deselect_by_index(value)
                logger.debug("下拉框已经通过 %s 元素取消选择元素" % type)
            elif type == 'value':
                Select(ele).deselect_by_value(value)
                logger.debug("下拉框已经通过 %s 元素取消选择元素" % type)
            elif type == 'text':
                Select(ele).deselect_by_visible_text(value)
                logger.debug("下拉框已经通过 %s 元素取消选择元素" % type)
            elif type == 'all':
                Select(ele).deselect_all()
                logger.debug("下拉框已经通过 %s 元素取消所有选择元素" % type)
        except BaseException as e:
            logger.error("下拉框选择元素失败,reason : %s" % e)
            return False

    def get_Alert(self, methods, keys=0):
        try:
            if methods == 'accept':
                self.driver.switch_to_alert().accept()
                logger.debug("alert的确认操作...")
            elif methods == 'dismiss':
                self.driver.switch_to_alert().dismiss()  # 相当于点击x按钮
                logger.debug("alert的取消操作...")
            elif methods == 'send_keys':
                # 输入文本值 --仅限于 prompt,在 alert 和 confirm 上没有输入框
                self.driver.switch_to_alert().send_keys(keys)
                logger.debug("alert上输入文本值...")
            elif methods == 'text':
                return self.driver.switch_to_alert().text()  # 获取alert上的文本值
        except BaseException as e:
            logger.error("操作alert失败 reason:%s" % e)

    # cookies的增删改查
    def cookie(self, *args, **kwargs):
        type = args[0]
        if type == 'all':
            cookies = self.driver.get_cookies()
            logger.debug("获取所有cookies...")
            return cookies
        elif type == 'get':
            c_name = args[1]
            cookie = self.driver.get_cookie(c_name)
            logger.debug("获取 %s 的cookie" % c_name)
            return cookie
        elif type == 'del':
            self.driver.delete_cookie()
            logger.debug("删除当前cookies...")
        elif type == 'del_all':
            self.driver.delete_all_cookies()
            logger.debug("删除所有cookies...")
        elif type == 'add':
            self.driver.add_cookie(cookie_dict=kwargs)  # 可用于绕过验证码操作
            logger.debug("添加cookie值...")

    # 显示等待
    def web_driver_wait(self, seconds, selector):
        WebDriverWait(self.driver, seconds).until(
            EC.visibility_of_element_located(selector))
        logger.debug("显示等待 %d 秒 判断元素 %s 是否出现" % (seconds, selector))

    def web_driver_wait_title(self, seconds, value):
        WebDriverWait(self.driver, seconds).until(EC.title_is(value))
        logger.debug("显示等待 %d 秒 判断标题是否是 %s" % (seconds, value))

    def web_driver_wait_alert(self, seconds):
        WebDriverWait(self.driver, seconds).until(EC.alert_is_present())
        logger.debug("显示等待 %d 秒 判断alert是否出现" % seconds)

    def web_driver_wait_until_not(self, seconds, selector):
        WebDriverWait(self.driver, seconds).until_not(
            EC.presence_of_element_located(selector))
        logger.debug("显示等待 %d 秒 判断元素是否不在出现" % seconds)

    def web_driver_wait_visibility(self, seconds, selector):
        WebDriverWait(
            self.driver, seconds).until(
            EC.visibility_of(
                self.driver.find_element(selector)))
        logger.debug("显示等待 %d 秒 判断元素 %s 是否可见" % (seconds, selector))

    def web_driver_waitext(self, seconds, selector, value):
        WebDriverWait(
            self.driver, seconds).until(
            EC.text_to_be_present_in_element(
                selector, value))
        logger.debug("显示等待 %d 秒 判断元素值 %s 是否出现" % (seconds, value))

    def sleep(self, seconds):
        time.sleep(seconds)
        logger.debug("强制等待 %d 秒" % seconds)
